﻿
using PSE.Framework.Communication.Configuration.Adapters.XmlHttp;
using PSE.Framework.Communication.Adapters;
using PSE.Framework.Communication.Configuration.Adapters;
using PSE.Framework.Communication.Messages;
using System;
using System.Collections.Generic;
using System.Xml.Serialization;
using System.IO;
using System.Globalization;
using System.Net;
using System.Xml;
using PSE.Framework.Communication.Configuration.Exceptions;
using System.Text;
using PSE.Framework.Communication.Configuration.XmlHttp;
using System.Collections.Specialized;
using System.Configuration;

namespace PSE.Framework.Communication.Adapters.XmlHttp 
{
    public class XmlHttpOutputAdapter : ResponseOutputAdapter<XmlHttpOutputConfiguration, object>
    {
        private Uri _url = null;
        private ICredentials _credentials = null;
        private ICredentials _proxyCredentials = null;

        private Dictionary<string, XmlSerializer> _serializers;
        private Dictionary<string, XmlSerializer> _deserializers;

        public XmlHttpOutputAdapter()
        {

        }

        public override void Open(XmlHttpOutputConfiguration config, object parameter)
        {
            if (config == null)
                throw new ArgumentNullException("config");

            this.VerifyIsCreated();

            // Capturado url e credenciais do arquivo de configuração
            _url = config.Url;
            _credentials = config.Credentials.GetCredential();
            _proxyCredentials = config.ProxyCredentials.GetCredential();

            _serializers = new Dictionary<string, XmlSerializer>();
            _deserializers = new Dictionary<string, XmlSerializer>();

            // Criando deserializers
            foreach (XmlHttpOutputAdapterActionConfiguration action in config.Actions)
            {
                if (action.DeserializedMessageType != null)
                {
                    _deserializers.Add(action.Name, new XmlSerializer(action.DeserializedMessageType));
                }
            }

            base.Open(config, parameter);
        }

        protected override object Send(object message)
        {
            string responseMessage = null;

            using (WebClient webClient = new WebClient())
            {
                if (_credentials != null)
                    webClient.Credentials = _credentials;

                if (_proxyCredentials != null)
                    webClient.Proxy.Credentials = _proxyCredentials;

                if (message is NameValueCollection)
                {
                    byte[] responseBytes = webClient.UploadValues(_url, "POST", (NameValueCollection)message);
                    Encoding encoding = Encoding.GetEncoding(this.Configuration.Encoding);
                    responseMessage = encoding.GetString(responseBytes);
                }
                else
                    responseMessage = webClient.UploadString(_url, (string)message);
            }

            if (string.IsNullOrEmpty(responseMessage))
                throw new AdapterException("The returned message is empty", this.Configuration.Name, AdapterDirection.Output);

            return responseMessage;
        }

        protected override object Serialize(object message, string action)
        {
            object serializedMessage = message;

            // Verifica se mensagem não é uma string
            if (!(message is string) && !(message is NameValueCollection))
            {
                // Verifica se já existe um serializer para esta action
                if (!_serializers.ContainsKey(action))
                {
                    // Se não existir, cria serializer
                    _serializers.Add(action, new XmlSerializer(message.GetType()));
                }

                StringBuilder sb = new StringBuilder();

                XmlWriterSettings settings = new XmlWriterSettings();
                // Identa o xml
                settings.Indent = true;
                // Encoding UTF-8
                settings.Encoding = Encoding.GetEncoding(this.Configuration.Encoding);

                // Criando writer
                using (XmlWriter writer = XmlWriter.Create(sb, settings))
                {
                    // Remove as namespaces da tag root
                    XmlSerializerNamespaces namespaces = new XmlSerializerNamespaces();
                    namespaces.Add(string.Empty, string.Empty);

                    foreach (KeyValueConfigurationElement ns in this.Configuration.Namespaces)
                    {
                        namespaces.Add(ns.Key, ns.Value);
                    }


                    // Serializando objeto
                    _serializers[action].Serialize(writer, message, namespaces);

                    serializedMessage = sb.ToString();
                }
            }

            return serializedMessage;
        }

        protected override object Deserialize(object message, string action)
        {
            object deserializedMessage = message;

            if (this.Configuration.Actions.Count > 0)
            {
                StringReader reader;

                // Validando a mensagem
                if (!string.IsNullOrEmpty(this.Configuration.Actions[action].ValidationFile))
                {
                    XmlReaderSettings settings = new XmlReaderSettings();
                    settings.Schemas.Add(null, this.Configuration.Actions[action].ValidationFile);
                    settings.ValidationType = ValidationType.Schema;

                    reader = new StringReader(System.Convert.ToString(message));

                    // Percorre toda a mensagem para validar
                    using (XmlReader xmlReader = XmlReader.Create(reader, settings))
                    {
                        while (xmlReader.Read()) { }
                    }
                }

                // Se não tiver um type configurado retorna a string xml
                if (_deserializers.ContainsKey(action))
                {
                    reader = new StringReader(System.Convert.ToString(message));

                    deserializedMessage = _deserializers[action].Deserialize(reader);
                }                
            }

            return deserializedMessage;
        }
    }

}